package tjs.study.notes.dotest.zookper.locker.curator;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import tjs.study.notes.dotest.zookper.locker.common.RLock;
import tjs.study.notes.dotest.zookper.locker.common.RLockFactory;

import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
 * Created by Nicky.Tang on 2021/11/9 8:53 下午
 *
 * @since 02.12.10
 */
public class RLockCuratorFactory implements RLockFactory {
    private CuratorFramework zkClient;

    public RLockCuratorFactory(CuratorFramework zkClient) {
        this.zkClient = zkClient;
    }

    public RLock getLock(String key) {
        InterProcessMutex interProcessMutex = new InterProcessMutex(zkClient, key);
        final ConcurrentMap<Thread, Object> threadData = getThreadData(interProcessMutex);
        final Field lockPathF = getLockPathF();

        RLock rLock = new RLock() {
            @Override
            public void close() throws Exception {
                this.unlock();
            }

            @Override
            public String getLockPath() {
                try {
                    return (String) lockPathF.get(threadData.get(Thread.currentThread()));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public boolean lock() {
                try {
                    return interProcessMutex.acquire(99999l, TimeUnit.DAYS);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return false;
            }

            @Override
            public void unlock() {
                try {
                    interProcessMutex.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        return rLock;
    }

    private ConcurrentMap<Thread, Object> getThreadData(InterProcessMutex interProcessMutex) {
        Field threadDataF = null;
        try {
            threadDataF = InterProcessMutex.class.getDeclaredField("threadData");
            threadDataF.setAccessible(true);
            return (ConcurrentMap<Thread, Object>) threadDataF.get(interProcessMutex);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private Field getLockPathF() {
        Class lockDataC = null;
        try {
            lockDataC = Class.forName("org.apache.curator.framework.recipes.locks.InterProcessMutex$LockData");

            Field lockPathF = lockDataC.getDeclaredField("lockPath");
            lockPathF.setAccessible(true);
            return lockPathF;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
